# The MIT License
#
# Copyright (c) 2007 Leah Culver
# Copyright (c) 2009 Canonical, Ltd.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
'''
OAuth Server interface
'''
import urlparse

from poauth.client import OAuthRequest
from poauth.oauth import OAuthToken, OAUTH_VERSION

class OAuthResponse(OAuthRequest):
    '''Class to manage OAuthServer response data.'''

    def __init__(self, *args, **kwargs):
        super(OAuthResponse, self).__init__(*args, **kwargs)
        self.status_code = kwargs.get('status', 401)
        self.status_msg = kwargs.get('message', "Unauthorized")
        self.realm = kwargs.get('realm', None)
        self.header = {'WWW-Authenticate' : 'OAuth realm="%s"' % self.realm}
        self.callback = kwargs.pop('callback', None)
        if self.callback:
            self.status_code = 307
            self.header = {'Location' : self.callback}
        self.parameters = kwargs.get('parameters', {})
        try:
            self.token = OAuthToken(self.parameters['oauth_token'],
                                    self.parameters['oauth_token_secret'])
        except KeyError:
            self.token = None

    def headers(self):
        '''Return the set of headers to respond with.'''
        return self.header
        

class OAuthServer(object):
    '''Class for building an OAuth server.'''

    def __init__(self, storage, realm, supported_sig_methods):
        self.storage = storage
        self.realm = realm
        self.sig_methods = supported_sig_methods

    def get_request_token(self, request):
        '''Get a request token.'''
        required_params = ['oauth_consumer_key',
                           'oauth_signature_method',
                           'oauth_signature',
                           'oauth_timestamp',
                           'oauth_nonce',
                           'oauth_callback']
        supported_params = ['oauth_version']

        bad_request = OAuthResponse(status=400, message="Bad Request",
                                    realm=self.realm)
        parameters = request.parameters
        try:
            if parameters['oauth_version'] != OAUTH_VERSION:
                return bad_request
        except KeyError:
            pass

        if not self.__validate_required_params(required_params, parameters):
            return bad_request

        if parameters['oauth_signature_method'] not in self.sig_methods:
            return self.bad_request

        response_params = {}
        callback = parameters['oauth_callback']
        if callback == 'oob':
            response_params['oauth_callback_confirmed'] = 'true'
        else:
            try:
                parts = urlparse.urlparse(callback)
                response_params['oauth_callback_confirmed'] = 'true'
            except:
                return bad_request

        consumer_key = parameters['oauth_consumer_key']
        consumer = self.storage.lookup_consumer(consumer_key)
        if not consumer:
            return OAuthResponse(realm=self.realm)

        response = self.storage.fetch_request_token(parameters)
        if response.status_code == 200:
            response.parameters.update(response_params)
        return response

    def authorize_request_token(self, request):
        '''Authorize the request token.'''
        required_params = ['oauth_token']

        parameters = request.parameters

        if not self.__validate_required_params(required_params, parameters):
            return OAuthResponse(status=400, message="Bad Request",
                                 realm=self.realm)

        token = self.storage.lookup_token(parameters['oauth_token'])
        if token:
            return self.storage.authorize_request_token(token, parameters)
        return OAuthResponse(realm=self.realm)

    def get_access_token(self, request):
        '''Get an access token.'''
        required_params = ['oauth_consumer_key',
                           'oauth_token',
                           'oauth_signature_method',
                           'oauth_signature',
                           'oauth_timestamp',
                           'oauth_nonce',
                           'oauth_verifier']
        supported_params = ['oauth_version']

        parameters = request.parameters

        if not self.__validate_required_params(required_params, parameters):
            return OAuthResponse(status=400, message="Bad Request",
                                 realm=self.realm)

        if parameters['oauth_signature_method'] not in self.sig_methods:
            return self.bad_request

        token = self.storage.lookup_token(parameters['oauth_token'])
        if token:
            return self.storage.fetch_access_token(token, parameters)
        return OAuthResponse(realm=self.realm)

    def __validate_required_params(self, required_params, parameters):
        '''Validate that all the required parameters are there.'''
        for param in required_params:
            try:
                exists = parameters[param]
            except KeyError:
                return False
        return True

class OAuthStorage(object):
    '''Class for storing and verifying OAuth data.'''

    def lookup_consumer(self, consumer_key):
        '''Validate the consumer key.'''
        raise NotImplementedError

    def lookup_token(self, token_key):
        '''Look up an existing OAuthToken.'''
        raise NotImplementedError

    def fetch_request_token(self, parameters):
        """-> OAuthResponse."""
        raise NotImplementedError

    def authorize_request_token(self, token, parameters):
        """-> OAuthResponse."""
        raise NotImplementedError

    def fetch_access_token(self, token, parameters):
        """-> OAuthResponse."""
        raise NotImplementedError
